package nodes;

import java.util.ArrayList;

public class Type
{
    public static final Type STR = new Type("str");
    public static final Type INT = new Type("int");
    public static final Type FLOAT = new Type("float");
    public static final Type CHAR = new Type("char");
    public static final Type BOOL = new Type("bool");
    public static final Type TUPLE = new Type("tuple");
    public static final Type LIST = new Type("list");

    public static final Type VOID = new Type("void");

    public static final Type UNDEF = new Type("_undef");

    private String type;
    private static final ArrayList<Type> basicTypes = new ArrayList<Type>();
    static {
        basicTypes.add(STR);
        basicTypes.add(INT);
        basicTypes.add(FLOAT);
        basicTypes.add(CHAR);
        basicTypes.add(BOOL);
        basicTypes.add(TUPLE);
        basicTypes.add(LIST);
    }
    private static final ArrayList<Type> sequenceTypes = new ArrayList<Type>();
    static {
        sequenceTypes.add(STR);
        sequenceTypes.add(TUPLE);
        sequenceTypes.add(LIST);
    }
    private static final ArrayList<Type> userTypes = new ArrayList<Type>();

    private Type(String type)
    {
        this.type = type;
    }

    public String getType()
    {
        return type;
    }

    /*
     * Static methods.
     *
     */

    /**
     * Checks if this <code>type</code> is a basic type
     * i.e. string, int, float, char, bool, tuple or list.
     *
     * @param type <code>Type</code> to check
     * @return
     */
    public static boolean isBasic(Type type)
    {
        for (Type t : basicTypes)
        {
            if (t==type) // This is safe; Constructor is private
                return true;
        }
        return false;
    }

    /**
     * Checks if this <code>type</code> is user defined
     * i.e. it is not a basic type.
     *
     */
    public static boolean isUser(Type type)
    {
        return !isBasic(type);
    }

    /**
     * Checks if this <code>type</code> is a sequence type
     * i.e. string, tuple or list.
     *
     * @param type <code>Type</code> to check
     * @return
     */
    public static boolean isSequence(Type type)
    {
        for (Type t : sequenceTypes)
        {
            if (t==type) // This is safe; Constructor is private
                return true;
        }
        return false;
    }

    /**
     * Checks if this <code>type</code> is void.
     *
     * @param type <code>Type</code> to check
     * @return
     */
    public static boolean isVoid(Type type)
    {
        return VOID==type; // This is safe; Constructor is private
    }

    /**
     * Checks if this <code>type</code> is boolean.
     *
     * @param type <code>Type</code> to check
     * @return
     */
    public static boolean isBoolean(Type type)
    {
        return BOOL==type;
    }

    /**
     * Checks if this <code>type</code> is a number
     * i.e. float or int.
     *
     * @param type <code>Type</code> to check
     * @return
     */
    public static boolean isNumber(Type type)
    {
        return type == INT || type == FLOAT;
    }

    /**
     * Checks if this <code>type</code> is UNDEF.
     *
     * @param type <code>Type</code> to check
     * @return
     */
    public static boolean isUndef(Type type)
    {
        return type == UNDEF;
    }

    public static Type typeOf(String type)
    {
        if (type == null)
            throw new NullPointerException("Type cannot be null!");
        for (Type t : basicTypes)
        {
            if (t.type.equals(type))
                return t;
        }
        for (Type t : userTypes)
        {
            if (t.type.equals(type))
                return t;
        }
        Type userType = new Type(type);
        userTypes.add(userType);
        return userType;
    }

    @Override
    public String toString()
    {
        return type;
    }
}